/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */
package xowl.interpreter.backends.rete;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import xowl.interpreter.backends.rdf.RDFNode;
import xowl.interpreter.backends.rdf.RDFNodeType;
import xowl.interpreter.backends.rdf.RDFTriple;

/**
 *
 * @author Laurent WOUTERS
 */
public class StatisticalOptimization implements OptimizationStrategy {
    private static class Counter {
        private int count;
        public void inc() { count++; }
        public int getCount() { return count; }
    }
    
    private List<List<Condition>> originals;
    private Map<RDFTriple, Counter> counters;
    private Map<RDFNode, Map<RDFNode, Map<RDFNode, Counter>>> mapCounters;
    private int total;
    
    public StatisticalOptimization() { }
    
    @Override public List<List<Condition>> optimize(List<List<Condition>> originals) {
        this.originals = originals;
        this.counters = new HashMap<RDFTriple, Counter>();
        this.mapCounters = new HashMap<RDFNode, Map<RDFNode, Map<RDFNode, Counter>>>();
        
        for (List<Condition> conditions : this.originals)
            buildRule(conditions);
        List<List<Condition>> result = new ArrayList<List<Condition>>(this.originals.size());
        for (List<Condition> triples : this.originals)
            result.add(buildResult(triples));
        return result;
    }
    
    private void buildRule(List<Condition> conditions) {
        for (Condition cond : conditions) {
            Counter counter = getCounter(cond.getTriple());
            counter.inc();
            counters.put(cond.getTriple(), counter);
            total++;
        }
    }
    private List<Condition> buildResult(List<Condition> triples) {
        List<Condition> result = new ArrayList<Condition>(triples);
        Collections.sort(result, new Comparator<Condition>() {
            @Override public int compare(Condition t1, Condition t2) {
                float f1 = (float)counters.get(t1.getTriple()).getCount() / (float)total;
                float f2 = (float)counters.get(t2.getTriple()).getCount() / (float)total;
                if (f1 < f2) return -1;
                if (f2 < f1) return 1;
                return 0;
            }
        });
        return result;
    }
    
    private Counter getCounter(RDFTriple triple) {
        RDFNode subj = triple.getSubject();
        RDFNode prop = triple.getProperty();
        RDFNode obj = triple.getObject();
        if (subj.getNodeType() == RDFNodeType.Variable) subj = null;
        if (prop.getNodeType() == RDFNodeType.Variable) prop = null;
        if (obj.getNodeType() == RDFNodeType.Variable) obj = null;
        
        Map<RDFNode, Map<RDFNode, Counter>> mapProps = mapCounters.get(subj);
        if (mapProps == null) {
            mapProps = new HashMap<RDFNode, Map<RDFNode, Counter>>();
            mapCounters.put(subj, mapProps);
        }
        Map<RDFNode, Counter> mapObjs = mapProps.get(prop);
        if (mapObjs == null) {
            mapObjs = new HashMap<RDFNode, Counter>();
            mapProps.put(prop, mapObjs);
        }
        Counter counter = mapObjs.get(obj);
        if (counter == null) {
            counter = new Counter();
            mapObjs.put(obj, counter);
        }
        return counter;
    }
}
